﻿using Microsoft.AspNetCore.Http;
using System.IO;
using System.Linq;
using System.Web;

namespace FileManagerSystem.Extend
{
    /// <summary>
    /// httpContext扩展
    /// </summary>
    public static class HttpContextExtend
    {
        /// <summary>
        /// 获取用户访问端口
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string GetClientUserToken(this HttpContext context)
        {
            //主要是用于nginx等多级转发时获取用户真实ip，不然获取到的是nginx转发服务器的ip，具体要什么值要自己取到后自行操作
            var token = context.Request.Headers["Authorization"].FirstOrDefault();
            return token;
        }

        /// <summary>
        /// 获取客户Ip
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string GetClientUserIp(this HttpContext context)
        {
            //主要是用于nginx等多级转发时获取用户真实ip，不然获取到的是nginx转发服务器的ip，具体要什么值要自己取到后自行操作
            var ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (string.IsNullOrEmpty(ip))
            {
                ip = context.Connection.RemoteIpAddress.MapToIPv4().ToString();
            }
            return ip;
        }

        /// <summary>
        /// 获取用户访问端口
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string GetClientUserPort(this HttpContext context)
        {
            //主要是用于nginx等多级转发时获取用户真实ip，不然获取到的是nginx转发服务器的ip，具体要什么值要自己取到后自行操作
            var port = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (string.IsNullOrEmpty(port))
            {
                port = context.Connection.RemotePort.ToString();
            }
            return port;
        }

        /// <summary>
        /// 获取所请求服务器的公网ip地址
        /// </summary>
        /// <returns></returns>
        public static string GetServerHostIp(this HttpContext context)
        {
            var ip = context.Connection.LocalIpAddress.MapToIPv4().ToString();
            if (ip == "0.0.0.1")//本地调试时候去读公网网卡的ip
                                //此写法有个缺点，只能读第一个网卡，如果是云服务器有多个网卡，然后内网之间发生本地调用则会获取少了数据，
                                //但是一般情况下如果外网访问进来的ip，上一行的写法完全够用了
            {
                ip = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()
                    .Select(p => p.GetIPProperties()).SelectMany(p => p.UnicastAddresses)
                    .Where(p => p.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && !System.Net.IPAddress.IsLoopback(p.Address))
                    .FirstOrDefault()?.Address.ToString();
            }
            return ip;
        }

        /// <summary>
        /// 获取请求的服务器的后台端口
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string GetServerHostPort(this HttpContext context)
        {
            var port = context.Connection.LocalPort.ToString();
            return port;
        }

        /// <summary>
        /// 获取文件在服务器的位置
        /// </summary>
        /// <param name="context"></param>
        /// <param name="filePath">文件保存的相对位置</param>
        /// <returns></returns>
        public static string GetFileRealPath(this HttpContext context, string filePath)
        {
            if (filePath.IsNullOrWhiteSpace())
                return null;
            var path = $"{context.Request.Scheme}://{Path.Combine(context.Request.Host.Value, filePath).Replace("\\", "/")}";
            return path;
        }


        /// <summary>
        /// 通过文件流下载文件
        /// </summary>
        /// <param name="context"></param>
        /// <param name="fileName">文件名</param>
        /// <param name="filePath">文件完整路径</param>
        /// <param name="contentType">访问这里 https://tool.oschina.net/commons 默认是文件流</param>
        public static void DownLoadFile(this HttpContext context, string filePath, string fileName, string contentType = "application/octet-stream")
        {
            //var fileName = Path.GetFileName(filePath);

            int bufferSize = 1024;
            context.Response.ContentType = contentType;
            context.Response.Headers.Append("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(fileName));
            context.Response.Headers.Append("Charset", "utf-8");
            context.Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
            //context.Response.Headers.Append("Access-Control-Allow-Origin", "*");
            //使用FileStream开始循环读取要下载文件的内容
            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                using (context.Response.Body)
                {
                    long contentLength = fs.Length;
                    context.Response.ContentLength = contentLength;

                    byte[] buffer;
                    long hasRead = 0;
                    while (hasRead < contentLength)
                    {
                        if (context.RequestAborted.IsCancellationRequested)
                        {
                            break;
                        }

                        buffer = new byte[bufferSize];
                        //从下载文件中读取bufferSize(1024字节)大小的内容到服务器内存中
                        int currentRead = fs.Read(buffer, 0, bufferSize);
                        context.Response.Body.Write(buffer, 0, currentRead);
                        context.Response.Body.Flush();
                        hasRead += currentRead;
                    }
                    context.Response.Body.Close();
                }
                fs.Close();
            }
        }

        /// <summary>
        /// 通过文件流下载文件
        /// </summary>
        /// <param name="context"></param>
        /// <param name="fileName">文件名</param>
        /// <param name="fileByte">文件字节流</param>
        /// <param name="contentType">访问这里 https://tool.oschina.net/commons </param>
        public static void DownLoadFile(this HttpContext context, string fileName, byte[] fileByte, string contentType = "application/octet-stream")
        {
            int bufferSize = 1024;

            context.Response.ContentType = contentType;
            context.Response.Headers.Append("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(fileName));
            context.Response.Headers.Append("Charset", "utf-8");
            context.Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");

            //context.Response.Headers.Append("Access-Control-Allow-Origin", "*");
            //使用FileStream开始循环读取要下载文件的内容
            using (Stream fs = new MemoryStream(fileByte))
            {
                using (context.Response.Body)
                {
                    long contentLength = fs.Length;
                    context.Response.ContentLength = contentLength;

                    byte[] buffer;
                    long hasRead = 0;
                    while (hasRead < contentLength)
                    {
                        if (context.RequestAborted.IsCancellationRequested)
                        {
                            break;
                        }

                        buffer = new byte[bufferSize];
                        //从下载文件中读取bufferSize(1024字节)大小的内容到服务器内存中
                        int currentRead = fs.Read(buffer, 0, bufferSize);
                        context.Response.Body.Write(buffer, 0, currentRead);
                        context.Response.Body.Flush();
                        hasRead += currentRead;
                    }
                }
            }
        }
    }
}
